package server

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"log/slog"
	"net"
	"net/http"
	"quipus/internal/database"
	"quipus/internal/database/dao"
	"quipus/internal/profile"
	"quipus/internal/utils"
	"time"

	"github.com/go-faster/errors"
	"github.com/gofiber/fiber/v2"
	"github.com/soheilhy/cmux"
	"google.golang.org/grpc"
)

type FiberServer struct {
	*fiber.App

	db         dao.Driver
	secret     string
	profile    *profile.Profile
	grpcServer *grpc.Server
	gdsAccess  *dao.GdsAccess
}

func NewServer(ctx context.Context, profile *profile.Profile) (*FiberServer, error) {

	driver, handler, err := database.NewDBDriver(profile)
	if err != nil {
		panic(err)
	}

	gAccess := dao.New(driver, profile, handler)

	if profile.Mode != "demo" {
		if profile.CertFile == "" || profile.KeyFile == "" {
			var err error
			profile.CertFile, profile.KeyFile, err = utils.GenerateAndSaveCert()
			if err != nil {
				return nil, fmt.Errorf("failed to generate cert: %w", err)
			}
		}
	}

	server := &FiberServer{
		App: fiber.New(fiber.Config{
			ServerHeader:    "qps",
			AppName:         "quipus",
			BodyLimit:       1024 * 1024 * 1024,
			ReadBufferSize:  1024 * 1024,
			WriteBufferSize: 1024 * 1024,
		}),

		db:        driver,
		profile:   profile,
		gdsAccess: gAccess,
	}

	server.RegisterFiberRoutes(ctx)
	return server, nil
}

func (s *FiberServer) Start(ctx context.Context) error {
	address := fmt.Sprintf("%s:%d", s.profile.Addr, s.profile.Port)
	listener, err := net.Listen("tcp", address)
	if err != nil {
		return errors.Wrap(err, "failed to listen")
	}

	muxServer := cmux.New(listener)
	tlsListener := muxServer.Match(cmux.TLS())

	go func() {
		var grpcListener net.Listener
		if s.profile.Mode != "demo" && s.profile.CertFile != "" && s.profile.KeyFile != "" {

			grpcListener = cmux.New(tlsListener).MatchWithWriters(
				cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"),
			)
		} else {
			grpcListener = muxServer.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
		}

		if err := s.grpcServer.Serve(grpcListener); err != nil {
			slog.Error("failed to serve gRPC", "error", err)
		}
	}()
	go func() {

		if s.profile.CertFile != "" && s.profile.KeyFile != "" && s.profile.Mode != "demo" {
			cert, err := tls.LoadX509KeyPair(s.profile.CertFile, s.profile.KeyFile)
			if err != nil {
				slog.Error("failed to load TLS cert", "error", err)
				return
			}
			tlsConfig := &tls.Config{
				Certificates: []tls.Certificate{cert},
				MinVersion:   tls.VersionTLS12,   // 强制 TLS 1.2+
				ClientAuth:   tls.NoClientCert,   // 不需要客户端证书
				ClientCAs:    x509.NewCertPool(), // 空根证书池（无需验证客户端）
			}
			tlsListener := tls.NewListener(tlsListener, tlsConfig)
			if err := s.App.Listener(tlsListener); err != nil {
				slog.Error("failed to start fiber TLS server", "error", err)
			}
		} else {
			// 其他非 TLS 连接
			// otherListener := muxServer.Match(cmux.Any())
			httpListener := muxServer.Match(cmux.HTTP1Fast(http.MethodPatch))
			s.App.Listener(httpListener)
		}
	}()
	go func() {
		if err := muxServer.Serve(); err != nil {
			slog.Error("mux server listen error", "error", err)
		}
	}()
	// s.StartBackgroundRunners(ctx)

	return nil
}

func (s *FiberServer) Shutdown(ctx context.Context) {
	ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()

	// Shutdown echo server.

	if err := s.App.Shutdown(); err != nil {
		fmt.Printf("failed to shutdown server, error: %v\n", err)
	}

	// Close database connection.
	if err := s.db.Close(); err != nil {
		fmt.Printf("failed to close database, error: %v\n", err)
	}

	fmt.Printf("qp stopped properly\n")
}
